package es.efor.ics.gev.model;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Version;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

/**
 * Un {@link AtributoMaestro atributo} asociado a una {@link Fase fase} + obligatoriedad + orden
 * 
 * @see Fase
 * @see AtributoMaestro
 */
@Entity
@Table(name = "ATRIBUTOS_FASES")
public class AtributoFase extends BaseObject implements Serializable, AtributoAsignado {

	/**
	 * Embedded composite identifier class that represents the primary key
	 * columns of the many-to-many join table.
	 */
	@Embeddable
	public static class Id implements Serializable {		
		private static final long serialVersionUID = 3189809922522905264L;
		
		private Long faseId;
		private Long atributoId;
		
		public Id() {			
		}

		public Id(Long faseId, Long atributoId) {		
			this.faseId = faseId;
			this.atributoId = atributoId;
		}

		@Column(name = "FASE_ID")
		public Long getFaseId() {
			return faseId;
		}

		public void setFaseId(Long faseId) {
			this.faseId = faseId;
		}

		@Column(name = "ATRIBUTO_ID")
		public Long getAtributoId() {
			return atributoId;
		}

		public void setAtributoId(Long atributoId) {
			this.atributoId = atributoId;
		}
		
		@Override
		public boolean equals(Object o) {
			if (o instanceof Id) {
				Id that = (Id) o;
				return this.faseId.equals(that.faseId)
						&& this.atributoId.equals(that.atributoId);
			} else {
				return false;
			}
		}

		@Override
		public int hashCode() {
			return faseId.hashCode() + atributoId.hashCode();
		}
		
		@Override
		public String toString() {			
			return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE)
			.append("faseId", this.getFaseId())
			.append("atributoId", this.getAtributoId())
					.toString();
		}
		
	}
	
	// static data
	private static final long serialVersionUID = 7781806743583588115L;
	
	// private instance data
	private Id id;
	private Fase fase;
	private AtributoMaestro atributo;
	private boolean obligatorio;
	private Integer orden;
	private Integer version;
	
	/**
	 * Construct an uninitialized <code>AtributoFase</code>.
	 */
	protected AtributoFase() {		
	}
	
	/**
	 * Construct an initialized <code>AtributoFase</code>.
	 * 
	 * @param fase
	 * @param atributo
	 * @param obligatorio
	 * @param orden
	 */
	public AtributoFase(Fase fase, AtributoMaestro atributo,
			boolean obligatorio, Integer orden) {		
		this.fase = fase;
		this.atributo = atributo;
		this.obligatorio = obligatorio;
		this.orden = orden;
		
		// Set primary key
		this.id = new Id(fase.getId(), atributo.getId());
	}

	// ********************** Accessor Methods ********************** //
	
	@EmbeddedId
	public Id getId() {
		return id;
	}

	public void setId(Id id) {
		this.id = id;
	}
	
	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "FASE_ID", insertable = false, updatable = false)
	@org.hibernate.annotations.ForeignKey(name = "FK_ATRIB_FASES_FASE_ID")
	public Fase getFase() {
		return fase;
	}

	public void setFase(Fase fase) {
		this.fase = fase;
	}

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "ATRIBUTO_ID", insertable = false, updatable = false)
	@org.hibernate.annotations.ForeignKey(name = "FK_ATRIB_FASES_ATRIBUTO_ID")
	public AtributoMaestro getAtributo() {
		return atributo;
	}
	
	public void setAtributo(AtributoMaestro atributo) {
		this.atributo = atributo;
	}
	
	@Column(name = "OBLIGATORIO")
	public boolean isObligatorio() {
		return obligatorio;
	}

	public void setObligatorio(boolean obligatorio) {
		this.obligatorio = obligatorio;
	}
	
	@Column(name = "ORDEN")
	public Integer getOrden() {
		return orden;
	}

	public void setOrden(Integer orden) {
		this.orden = orden;
	}
	
	@Version
	@Column(name = "OBJ_VERSION")
	public Integer getVersion() {
		return version;
	}

	public void setVersion(Integer version) {
		this.version = version;
	}
	
	// ********************** Common Methods ********************** //
	
	@Override
	public int hashCode() {
		return id.hashCode();
	}

	@Override
	public boolean equals(Object o) {
		if (this == o)
			return true;
		if (o == null || getClass() != o.getClass())
			return false;
		final AtributoFase that = (AtributoFase) o;
		return id.equals(that.id);
	}
	
	@Override
	public String toString() {
		return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE).append(
				"fase", this.getFase()).append("atributo",
				this.getAtributo()).append("valor", this.isObligatorio())
				.append("orden", this.getOrden())
				.toString();
	}

	/**
	 * {@inheritDoc} 
	 * 
	 * @see java.lang.Comparable#compareTo(java.lang.Object)
	 */
	public int compareTo(Object o) {
		AtributoFase aThat = (AtributoFase) o;
		
		final int BEFORE = -1;
		final int EQUAL = 0;
		final int AFTER = 1;

		// this optimization is usually worthwhile, and can
		// always be added
		if (this == aThat)
			return EQUAL;

		// primitive numbers follow this form
		if (this.orden < aThat.orden)
			return BEFORE;
		if (this.orden > aThat.orden)
			return AFTER;

		// all comparisons have yielded equality
		// verify that compareTo is consistent with equals (optional)
		assert this.equals(aThat) : "compareTo inconsistent with equals.";

		return EQUAL;		
	}
	
}
